package com.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 消费者 基于接口的动态代理
 */
public class client {
    public static void main(String[] args) {
        /**
         * 动态代理
         *  特点：随用随创建，随用随加载。与装饰者不同的是，装饰者必须写好一个类。
         *  作用：不修改源码，对方法加强
         *  分类： 基于接口
         *        基于子类
         *  基于接口：jdk官方java.lang.reflect提供的Proxy,写完之后用接口进行接收。
         *  1、如何创建代理对象。
         *      使用Proxy类中newProxyInstance方法
         *      Proxy.newProxyInstance();
         *  2、创建代理对象的要求
         *      被代理类最少实现一个接口，如果没有则不能使用。
         *  3、newProxyInstance的参数
         *      classloader 固定写法：producer.class.getClassLoader()。用于加载代理对象字节码的，写的是被代理对象（producer）的类加载器.
         *      class[] 固定写法：producer.getClass().getInterfaces()。让代理和被代理对象有相同的方法。
         *      InvocationHandler: new InvocationHandler()，只有一个invoke方法。用于增强的代码，用于如何代理，一般是该接口的实现类，谁用谁写，通常情况下匿名内部类，但不是必须。
         */
        final  /*因为在匿名内部类中使用，因此需要加上final修饰*/ Producer producer=new Producer();//被代理对象。
        //前面的两个参数是固定写法、
        IProducer iProducer=(IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(), producer.getClass().getInterfaces(), new InvocationHandler() {
            /**
             * 作用：执行被代理对象的任何接口方法都会经过该方法。
             * @param proxy 代理对象的引用，一般不用
             * @param method 当前执行的方法。
             * @param args 当前执行方法的参数
             * @return 和被代理对象具有相同的返回值。
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /**
                 * 提供增强的代码
                 */
                Object returnvalue=null;
                //1获取方法的执行参数。
                Float money=(Float)args[0];//找到接口中定义的方法
                //2判断当前方法是不是销售
                if("saleProduct".equals(method.getName())){
                    returnvalue=method.invoke(producer,0.8f*money);
                }else if ("afterService".equals(method.getName())){
                    returnvalue=method.invoke(producer,0.5f*money);
                }
               // return method.invoke(producer,args);//这种写法，没有代理。
                return returnvalue;
            }
        });
        //注意这里已经使用的动态代理返回的对象去给saleproduct传参
        iProducer.saleProduct(10000f);
        iProducer.afterService(10000f);
    }
}
